from dataclasses import dataclass
from math import isclose
from typing import List, Literal, Optional, Tuple, Union
from MelodieFuncFlow import MelodieGenerator
from .fontsize_mapping import cn_font_size_to_pt
from ..utils import is_float
from dataclasses_json import dataclass_json
from .inline_shapes import InlineShapeInfo

CM_TO_POINTS = 28.35


@dataclass_json
@dataclass
class Position:
    left: int
    top: int
    width: int
    height: int


@dataclass_json
@dataclass
class MarginInfo:
    left: float
    right: float
    top: float
    bottom: float

    @classmethod
    def fromPageSetup(cls, pageSetup):
        return cls.fromPoints(
            pageSetup.LeftMargin,
            pageSetup.RightMargin,
            pageSetup.TopMargin,
            pageSetup.BottomMargin,
        )

    @classmethod
    def fromPoints(cls, l: float, r: float, t: float, b: float):
        self = cls(
            round(l / CM_TO_POINTS, 2),
            round(r / CM_TO_POINTS, 2),
            round(t / CM_TO_POINTS, 2),
            round(b / CM_TO_POINTS, 2),
        )
        return self

    def __eq__(self, obj: "MarginInfo"):
        assert isinstance(obj, self.__class__), (obj, self.__class__)
        if (
            self.left == obj.left
            and self.right == obj.right
            and self.top == obj.top
            and self.bottom == obj.bottom
        ):
            return True
        else:
            return False


@dataclass_json
@dataclass
class RangeInfo:
    startPage: int
    endPage: int


@dataclass_json
@dataclass
class PageRangeInfo:
    startPage: int
    endPage: int

    def to_tuple(self):
        return (self.startPage, self.endPage)

@dataclass_json
@dataclass
class Distance:
    value: float
    type: Union[Literal["char", "pt"], None]

    @classmethod
    def from_cm(cls, value: float) -> "Distance":
        return Distance(CM_TO_POINTS * value,  "pt")

    def __eq__(self, __value: "Distance") -> bool:
        assert isinstance(__value, Distance)
        if self.type != __value.type:
            return False
        return isclose(self.value, __value.value, abs_tol=0.05)


@dataclass_json
@dataclass
class FontSize:
    value: float
    alias: str = ""

    @classmethod
    def from_str(cls, font_str: str):
        self = cls(0.0)
        if is_float(font_str):
            self.value = float(font_str)
        else:
            self.value = cn_font_size_to_pt(font_str)
            self.alias = font_str
        return self

    def __repr__(self) -> str:
        return f"<FontSize {self.alias if self.alias else self.value}>"


@dataclass_json
@dataclass
class FontInfo:
    type: Literal['cn', 'western']
    name: str
    size: FontSize
    bold: bool
    italic: bool

@dataclass_json
@dataclass
class StyleInfo:
    name: str


@dataclass_json
@dataclass
class SpecialIndentationFormatInfo:
    type: Optional[Literal["first_line", "hanging"]]
    value: Optional[Distance] = None

    @classmethod
    def from_indentation_value(
        cls, value: float, type: Literal["char", "hanging"]
    ) -> "SpecialIndentationFormatInfo":
        indent_type = lambda val: "first_line" if val > 0 else "hanging"
        return cls(
            indent_type(value),
            Distance(abs(value), type),
        )


@dataclass_json
@dataclass
class IndentationInfo:
    special: Optional[SpecialIndentationFormatInfo]
    left: Distance
    right: Distance


@dataclass_json
@dataclass
class ParagraphInfo:
    """
    提取段(Paragraph)的信息
    """

    indentations: IndentationInfo  # 首行缩进的字符
    font: List[FontInfo]
    text: str
    style: StyleInfo
    inline_shapes: InlineShapeInfo
    position: Position
    page_range: PageRangeInfo


@dataclass_json
@dataclass
class SectionInfo:
    """
    提取节(Section)的信息
    """

    sectionNumber: int
    margin: MarginInfo
    range: RangeInfo
    paragraphs: List[ParagraphInfo]


@dataclass_json
@dataclass
class DocumentInfo:
    """
    提取文档(Document)的信息
    """

    sections: List[SectionInfo]

    def iter_paragraphs(self) -> MelodieGenerator[Tuple[SectionInfo, ParagraphInfo]]:
        def _():
            for section in self.sections:
                for paragraph in section.paragraphs:
                    yield section, paragraph

        return MelodieGenerator(_())
